/**
 * 版权所有：厦门市巨龙信息科技有限公司
 * Copyright 2024 Xiamen Dragoninfo Eng. Co. Ltd.
 * All right reserved.
 */
package com.github.alfred.configuration;

import org.apache.http.client.HttpClient;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.HostnameVerifier;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 类说明
 *
 * @author chengh
 * @date 2024/1/11 13:54
 */
@Configuration
public class RestTemplateConfig {
    private static final String HTTP = "http";
    private static final String HTTPS = "https";

    @Value("${spring.http-client.connect-timeout:60000}")
    private int connectTimeout;

    @Value("${spring.http-client.connect-request-timeout:60000}")
    private int connectionRequestTimeout;

    @Value("${spring.http-client.read-timeout:60000}")
    private int readTimeout;

    /**
     * 默认做过优化的 RestTemplate
     *
     * @return org.springframework.web.client.RestTemplate
     * @throws Exception java.lang.Exception
     */
    @Bean
    @Primary
    public RestTemplate restTemplate() throws Exception {
        return getRestTemplate();
    }


    /**
     * 获取多例状态的RestTemplate，用于设置定制化配置，避免配置污染
     * 使用这个方案，性能上必定有一定的影响
     * 如果不对RestTemplate做定制化操作， 建议直接使用 @Resource  RestTemplate  获取全局单例的 RestTemplate
     *
     * @return RestTemplate
     */
    @Bean("restTemplateForPrototype")
    @Scope("prototype")
    public RestTemplate restTemplateForPrototype() throws Exception {
        return getRestTemplate();
    }


    /**
     * 对RestTemplate 做统一的配置优化
     *
     * @return org.springframework.web.client.RestTemplate
     * @throws Exception java.lang.Exception
     */
    private RestTemplate getRestTemplate() throws Exception {
        SSLContextBuilder builder = new SSLContextBuilder();
        // 全部信任
        builder.loadTrustMaterial(null, (x509Certificates, s) -> true);
        // 所有服务器证书都允许
        HostnameVerifier hostnameVerifier = (s, sslSession) -> true;
        SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(builder.build(), hostnameVerifier);
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register(HTTP, new PlainConnectionSocketFactory())
                .register(HTTPS, sslFactory)
                .build();
        // 长连接保持30秒
        PoolingHttpClientConnectionManager pollingConnectionManager = new PoolingHttpClientConnectionManager(registry,
                null, null, null, 30, TimeUnit.SECONDS);
        // 总连接数
        pollingConnectionManager.setMaxTotal(1000);
        // 同路由的并发数
        pollingConnectionManager.setDefaultMaxPerRoute(1000);
        HttpClientBuilder httpClientBuilder = HttpClients.custom();
        httpClientBuilder.setConnectionManagerShared(true);
        httpClientBuilder.setConnectionManager(pollingConnectionManager);
        // 重试次数，默认是3次，没有开启

        // 保持长连接配置，需要在头添加 Keep-Alive
        httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
        HttpClient httpClient = httpClientBuilder.build();
        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        // 连接不够用的等待时间，必须设置，连接不够用时，时间过长可能是灾难性的
        clientHttpRequestFactory.setConnectionRequestTimeout(connectionRequestTimeout);
        clientHttpRequestFactory.setConnectTimeout(connectTimeout);
        clientHttpRequestFactory.setReadTimeout(readTimeout);
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);

        // 对中文格式数据进行处理
        FormHttpMessageConverter fc = new FormHttpMessageConverter();
        StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        List<HttpMessageConverter<?>> partConverters = new ArrayList<>();
        partConverters.add(stringConverter);
        partConverters.add(new ResourceHttpMessageConverter());
        fc.setPartConverters(partConverters);
        restTemplate.getMessageConverters().addAll(Arrays.asList(fc, new MappingJackson2HttpMessageConverter()));
        return restTemplate;
    }

}
